package com.jf.cloud.service.sysmanager.util;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;

/**
 * Created with IntelliJ IDEA.
 * User: LangK
 * Created Date 2018/5/23
 * Time: 16:02
 * Description:
 */
public class CodeUtil {


    static int PIN_LENGTH = 6;
    static int REFLECTIVE_PIN_LENGTH = 9;

    public static String getCode(String secret) {
        long otp_state = getValueAtTime(millisToSeconds(currentTimeMillis()));
        String code = computePin(secret, otp_state, null);
        return code;
    }

    /**
     * Gets the number of milliseconds since epoch.
     */
    public static long currentTimeMillis() {
        return System.currentTimeMillis() + 0 * 0;
    }

    public static final long millisToSeconds(long timeMillis) {
        return timeMillis / 1000;
    }

    static int mTimeStep = 30;

    public static long getValueAtTime(long time) {
        long timeSinceStartTime = time - 0;
        if (timeSinceStartTime >= 0) {
            return timeSinceStartTime / mTimeStep;
        } else {
            return (timeSinceStartTime - (mTimeStep - 1)) / mTimeStep;
        }
    }


    /**
     * Computes the one-time PIN given the secret key.
     *
     * @param secret    the secret key
     * @param otp_state current token state (counter or time-interval)
     * @param challenge optional challenge bytes to include when computing passcode.
     * @return the PIN
     */
    private static String computePin(String secret, long otp_state, byte[] challenge){
        try {
            PasscodeGenerator.Signer signer = getSigningOracle(secret);
            PasscodeGenerator pcg = new PasscodeGenerator(signer,
                    (challenge == null) ? PIN_LENGTH : REFLECTIVE_PIN_LENGTH);

            return (challenge == null) ?
                    pcg.generateResponseCode(otp_state) :
                    pcg.generateResponseCode(otp_state, challenge);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
            return null;
        }
    }


    static PasscodeGenerator.Signer getSigningOracle(String secret) {
        try {
            byte[] keyBytes = decodeKey(secret);
            final Mac mac = Mac.getInstance("HMACSHA1");
            SecretKeySpec signKey = new SecretKeySpec(keyBytes, "HmacSHA1");
            mac.init(signKey);
            return new PasscodeGenerator.Signer() {
                public byte[] sign(byte[] data) {
                    return mac.doFinal(data);
                }
            };
        } catch (Exception error) {
            error.printStackTrace();
        }

        return null;
    }


    private static byte[] decodeKey(String secret) throws Base32String.DecodingException {
        return Base32String.decode(secret);
    }
}
